package com.sensoro.beacon.kit;

import android.util.Base64;

import com.sensoro.beacon.kit.BaseSettings.TransmitPower;
import com.sensoro.beacon.kit.BaseSettings.EnergySavingMode;
import com.sensoro.beacon.kit.BaseSettings.AdvertisingInterval;
import com.sensoro.beacon.kit.BaseSettings.SecureBroadcastInterval;
import com.sensoro.beacon.kit.SensorSettings.AccelerometerSensitivity;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * Created by Sensoro on 15/1/16.
 */
class SensoroUtils {
	private static final char[] HEX_CHAR_TABLE = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
	private static final byte[] HEX_TABLE = { 0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF };

	private static final int FIVE_SECONDS = 5;
	private static final int ONE_MINUTE = 60;
	private static final int ONE_HOUR = 3600; // 60*60
	private static final int ONE_DAY = 86400; // 60*60*24
	private static final int SEVEN_DAYS = 604800; // 60*60*24*7
	private static final int THIRTY_DAYS = 2592000; // 60*60*24*30

	public static TransmitPower getTransmitPower(int i) {
		switch (i) {
		case 0:
			return TransmitPower.LEVEL0;
		case 1:
			return TransmitPower.LEVEL1;
		case 2:
			return TransmitPower.LEVEL2;
		case 3:
			return TransmitPower.LEVEL3;
		case 4:
			return TransmitPower.LEVEL4;
		case 5:
			return TransmitPower.LEVEL5;
		case 6:
			return TransmitPower.LEVEL6;
		case 7:
			return TransmitPower.LEVEL7;
		case 8:
			return TransmitPower.LEVEL8;
		case 9:
			return TransmitPower.LEVEL9;
		case 10:
			return TransmitPower.LEVEL10;
		case 11:
			return TransmitPower.LEVEL11;
		default:
			return TransmitPower.UNKNOWN;
		}
	}

	public static EnergySavingMode getEnergySavingMode(int i) {
		switch (i) {
		case 0:
			return EnergySavingMode.NONE;
		case 1:
			return EnergySavingMode.LIGHT_SENSOR;
		default:
			return EnergySavingMode.UNKNOWN;
		}
	}

	public static AccelerometerSensitivity getAccleromerterSensitivity(int i) {
		switch (i) {
		case 0:
			return AccelerometerSensitivity.DISABLED;
		case 0x70:
			return AccelerometerSensitivity.MIN;
		case 0x5d:
			return AccelerometerSensitivity.MEDIUM;
		case 0x4b:
			return AccelerometerSensitivity.MAX;
		default:
			return AccelerometerSensitivity.UNKNOWN;
		}
	}

	public static AdvertisingInterval getAdvertisingInterval(int i) {
		switch (i) {
		case 0:
			return AdvertisingInterval.ADVERTISING_INTERVAL_100;
		case 1:
			return AdvertisingInterval.ADVERTISING_INTERVAL_152_5;
		case 2:
			return AdvertisingInterval.ADVERTISING_INTERVAL_211_25;
		case 3:
			return AdvertisingInterval.ADVERTISING_INTERVAL_318_75;
		case 4:
			return AdvertisingInterval.ADVERTISING_INTERVAL_417_5;
		case 5:
			return AdvertisingInterval.ADVERTISING_INTERVAL_546_25;
		case 6:
			return AdvertisingInterval.ADVERTISING_INTERVAL_760;
		case 7:
			return AdvertisingInterval.ADVERTISING_INTERVAL_852_5;
		case 8:
			return AdvertisingInterval.ADVERTISING_INTERVAL_1022_5;
		case 9:
			return AdvertisingInterval.ADVERTISING_INTERVAL_1285;
		default:
			return AdvertisingInterval.UNKNOWN;
		}
	}

	public static int getSecureBroadcastIntervalInt(BaseSettings.SecureBroadcastInterval secureBroadcastInterval) {
		switch (secureBroadcastInterval) {
		case UNKNOWN:
			return 0;
		case NONE:
			return 0;
		case SECURE_BROADCAST_INTERVAL_5_SECONDS:
			return FIVE_SECONDS;
		case SECURE_BROADCAST_INTERVAL_1_MINTE:
			return ONE_MINUTE;
		case SECURE_BROADCAST_INTERVAL_1_HONR:
			return ONE_HOUR;
		case SECURE_BROADCAST_INTERVAL_1_DAY:
			return ONE_DAY;
		case SECURE_BROADCAST_INTERVAL_7_DAYS:
			return SEVEN_DAYS;
		case SECURE_BROADCAST_INTERVAL_30_DAYS:
			return THIRTY_DAYS;
		default:
			return 0;
		}
	}

	public static SecureBroadcastInterval getSecureBroadcastInterval(int secureBroadcastIntervalInt) {
		switch (secureBroadcastIntervalInt) {
		case 0:
			return SecureBroadcastInterval.NONE;
		case FIVE_SECONDS:
			return SecureBroadcastInterval.SECURE_BROADCAST_INTERVAL_5_SECONDS;
		case ONE_MINUTE:
			return SecureBroadcastInterval.SECURE_BROADCAST_INTERVAL_1_MINTE;
		case ONE_HOUR:
			return SecureBroadcastInterval.SECURE_BROADCAST_INTERVAL_1_HONR;
		case ONE_DAY:
			return SecureBroadcastInterval.SECURE_BROADCAST_INTERVAL_1_DAY;
		case SEVEN_DAYS:
			return SecureBroadcastInterval.SECURE_BROADCAST_INTERVAL_7_DAYS;
		case THIRTY_DAYS:
			return SecureBroadcastInterval.SECURE_BROADCAST_INTERVAL_30_DAYS;
		default:
			return SecureBroadcastInterval.UNKNOWN;
		}
	}

	/**
	 * 将指定字符串src，以每两个字符分割转换为16进制形式 如："2B44EFD9" -> byte[]{0x2B, 0×44, 0xEF,
	 * 0xD9}
	 * 
	 * @param src
	 *            String
	 * @return byte[]
	 */
	public static byte[] HexString2Bytes(String src) {
		int length = src.length() / 2;
		byte[] ret = new byte[length];
		byte[] tmp = src.getBytes();
		for (int i = 0; i < length; i++) {
			ret[i] = uniteBytes(tmp[i * 2], tmp[i * 2 + 1]);
		}
		return ret;
	}

	/**
	 * 将两个ASCII字符合成一个字节； 如："EF" -> 0xEF
	 * 
	 * @param src0
	 *            byte
	 * @param src1
	 *            byte
	 * @return byte
	 */
	public static byte uniteBytes(byte src0, byte src1) {
		byte _b0 = Byte.decode("0x" + new String(new byte[] { src0 })).byteValue();
		_b0 = (byte) (_b0 << 4);
		byte _b1 = Byte.decode("0x" + new String(new byte[] { src1 })).byteValue();
		byte ret = (byte) (_b0 ^ _b1);
		return ret;
	}

	/**
	 * HMacSHA512 加密
	 * 
	 * @param data
	 * @param passwordKey
	 * @return
	 */
	public static byte[] HMacSHA512(byte[] data, String passwordKey) {
		Mac shaMac;
		byte[] secretBytes = passwordKey.getBytes();
		byte[] signatureBytes = null;
		try {
			shaMac = Mac.getInstance("HmacSHA512");
			SecretKey secretKey = new SecretKeySpec(secretBytes, "HmacSHA512");
			shaMac.init(secretKey);
			signatureBytes = shaMac.doFinal(data);
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}

		return signatureBytes;
	}

	public static String bytesToHex(byte[] bytes) {
		char[] hexChars = new char[bytes.length * 2];
		int v;
		for (int j = 0; j < bytes.length; j++) {
			v = bytes[j] & 0xFF;
			hexChars[j * 2] = HEX_CHAR_TABLE[v >>> 4];
			hexChars[j * 2 + 1] = HEX_CHAR_TABLE[v & 0x0F];
		}
		return new String(hexChars);
	}

	public static byte[] hexToByte(String hexString) {
		if (hexString == null || hexString.length() == 0)
			return null;
		if (hexString.length() % 2 != 0)
			throw new RuntimeException();
		byte[] data = new byte[hexString.length() / 2];
		char[] chars = hexString.toCharArray();
		for (int i = 0; i < hexString.length(); i = i + 2) {
			data[i / 2] = (byte) (HEX_TABLE[getHexCharValue(chars[i])] << 4 | HEX_TABLE[getHexCharValue(chars[i + 1])]);
		}
		return data;
	}

	public static int getHexCharValue(char c) {
		int index = 0;
		for (char c1 : HEX_CHAR_TABLE) {
			if (c == c1) {
				return index;
			}
			index++;
		}
		return 0;
	}

	// 解密
	public static byte[] decrypt_AES_128(byte[] src, byte[] key) {
		byte[] original = null;
		// 判断Key是否正确
		if (key == null) {
			return null;
		}
		// 判断Key是否为16位
		if (key.length != 16) {
			return null;
		}
		try {
			SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
			Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
			cipher.init(Cipher.DECRYPT_MODE, skeySpec);
			original = cipher.doFinal(src);
		} catch (Exception e) {
			return null;
		}
		return original;
	}

	// AES256 解密
	public static String decrypt_AES_256(String src, String key) {
		// 判断Key是否正确
		if (key == null) {
			return null;
		}
		// 判断src是否正确
		if (src == null) {
			return null;
		}
		try {
			SecretKey secretKey = getKey(key);

			// IMPORTANT TO GET SAME RESULTS ON iOS and ANDROID
			final byte[] iv = new byte[16];
			Arrays.fill(iv, (byte) 0x00);
			IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);

			byte[] encrypedPwdBytes = Base64.decode(src, Base64.DEFAULT);
			// cipher is not thread safe
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
			cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
			byte[] decrypedValueBytes = (cipher.doFinal(encrypedPwdBytes));

			String decrypedValue = new String(decrypedValueBytes);
			return decrypedValue;
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * Generates a SecretKeySpec for given password
	 * 
	 * @param password
	 * @return SecretKeySpec
	 * @throws UnsupportedEncodingException
	 */
	private static SecretKeySpec getKey(String password) throws UnsupportedEncodingException {

		// You can change it to 128 if you wish
		int keyLength = 256;
		byte[] keyBytes = new byte[keyLength / 8];
		// explicitly fill with zeros
		Arrays.fill(keyBytes, (byte) 0x0);

		// if password is shorter then key length, it will be zero-padded
		// to key length
		byte[] passwordBytes = password.getBytes("UTF-8");
		int length = passwordBytes.length < keyBytes.length ? passwordBytes.length : keyBytes.length;
		System.arraycopy(passwordBytes, 0, keyBytes, 0, length);
		SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
		return key;
	}
}
